package com.yunbao.hujin.myjsfour;

import android.annotation.SuppressLint;
import android.content.DialogInterface;
import android.net.Uri;
import android.os.Build;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.JsPromptResult;
import android.webkit.JsResult;
import android.webkit.ValueCallback;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Button;
import android.widget.LinearLayout;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

/**
 * 本地html文件（基础实例）
 */
public class Main2Activity extends AppCompatActivity {

    private LinearLayout ｍLayout;
    private WebView mWebView;
    private Button mButton;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);

        ｍLayout = findViewById(R.id.mLayout);
        mWebView = findViewById(R.id.mWebview);
        mButton = findViewById(R.id.mButton);

        setWebSetting();

//        setWebChromeClient();
        setWebChromeClient2();    //与上述方法setWebChromeClient()不共存
        setListener();

        setWebJs();
        setWebViewClient();
    }



    @SuppressLint("SetJavaScriptEnabled")
    private void setWebSetting() {
        WebSettings webSettings = mWebView.getSettings();
        webSettings.setJavaScriptEnabled(true);       // 设置与Js交互的权限（默认是不允许）
        webSettings.setJavaScriptCanOpenWindowsAutomatically(true);// 设置允许JS弹窗

        String url = "file:///android_asset/javascript.html";

//        webSettings.setAllowFileAccess(false);//禁止加载 file 协议的文件,禁用的话不能加载本地html
//        webSettings.setAllowFileAccess(true); //此处不行
//        if (url.startsWith("file://")) {
//            webSettings.setJavaScriptEnabled(false);
//        } else {
//            webSettings.setJavaScriptEnabled(true);
//        }

        //设置缓存
        webSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK); //关闭webview中缓存(在可用的时候使用缓存资源，即使它们已经过期了。否则将从网络加载资源。)
        webSettings.setAppCacheEnabled(true);//开启 Application Caches 功能
        // 每个 Application 只调用一次 WebSettings.setAppCachePath()，WebSettings.setAppCacheMaxSize()
        String cacheDirPath = getFilesDir().getAbsolutePath() + this.getClass().getName()+"cache/";
        webSettings.setAppCachePath(cacheDirPath); //1. 设置  Application Caches 缓存目录
        webSettings.setAppCacheMaxSize(20 * 1024 * 1024);   // 2. 设置缓存大小


        // 先载入JS代码
        // 格式规定为:file:///android_asset/文件名.html
        mWebView.loadUrl(url);
    }


    /************-------Android调用JS的方法----------********************************************/
    private void setListener() {
        mButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 可以另开线程进行JS方法调用
//                mWebView.post(new Runnable() {
//                    @Override
//                    public void run() {
//                        // 注意调用的JS方法名要对应上
//                        // 调用javascript的callJS()方法
//                        if (Build.VERSION.SDK_INT < 18) {
//                            //方法一、
//                            mWebView.loadUrl("javascript:callJS()");
//                        } else {
//                            //方法二、evaluateJavascript
//                            //该方法比第一种方法效率更高、使用更简洁。
//                            //1. 因为该方法的执行不会使页面刷新，而第一种方法（loadUrl ）的执行则会。
//                            //2. Android 4.4 后才可使用
//                            mWebView.evaluateJavascript("javascript:callJS()", new ValueCallback<String>() {
//                                @Override
//                                public void onReceiveValue(String value) {
//                                    //此处为 js 返回的结果
//                                }
//                            });
//                        }
//                    }
//                });

                AndroidToJs.setLoadUrl(mWebView, "callJS()");
            }
        });
    }



    private void setWebChromeClient() {
        // 由于设置了弹窗检验调用结果,所以需要支持js对话框
        // webview只是载体，内容的渲染需要使用webviewChromClient类去实现
        // 通过设置WebChromeClient对象处理JavaScript的对话框
        mWebView.setWebChromeClient(new WebChromeClient() {  //可以不要{里的东东}

            //1. 设置响应js 的Alert()函数
            @Override
            public boolean onJsAlert(WebView view, String url, String message, final JsResult result) {
                AlertDialog.Builder builder = new AlertDialog.Builder(Main2Activity.this);
                builder.setTitle("Alert");
                builder.setMessage(message);
                builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        result.confirm();
                    }
                });
                builder.setCancelable(false);
                builder.create().show();
                return true;
            }

        });

    }



    /************-------JS调用Android的方法----------********************************************/

    /**
     * 方法一、通过addJavascriptInterface()将Java对象映射到JS对象
     * /参数1：Javascript对象名
     * 参数2：Java对象名
     *
     * 存在严重的漏洞问题
     */
    private void setWebJs() {
        //仅仅将Android对象和JS对象映射--存在严重的漏洞问题
        mWebView.addJavascriptInterface(new JsToAndroid(this, mWebView), "test");//JsToAndroid类对象映射到js的test对象
    }


    /**
     * 方法二、复写WebViewClient类的shouldOverrideUrlLoading方法
     * 1. Android通过 WebViewClient 的回调方法shouldOverrideUrlLoading ()拦截 url
     * 2.      解析该 url 的协议
     * 3.      如果检测到是预先约定好的协议，就调用相应方法
     *
     * 具体使用：
     *      步骤1：在JS约定所需要的Url协议 ：如：document.location="js://webview?arg1=111&arg2=222";
     *      步骤2：在Android通过WebViewClient复写shouldOverrideUrlLoading （）
     *
     *  优点：不存在方式1的漏洞【不需要返回值时，用此方法互调】
     *  缺点：JS获取Android方法的返回值复杂。
     */
    private void setWebViewClient() {
        mWebView.setWebViewClient(new WebViewClient(){

            //复写WebViewClient类的shouldOverrideUrlLoading方法
            @Override
            public boolean shouldOverrideUrlLoading(WebView view, String url) {
                // 步骤1：加载JS代码(上面已经有了)
                // 格式规定为:file:///android_asset/文件名.html
//                mWebView.loadUrl("file:///android_asset/javascript.html");

                // 步骤2：根据协议的参数，判断是否是所需要的url
                // 一般根据scheme（协议格式） & authority（协议名）判断（前两个参数）
                //假定传入进来的 url = "js://webview?arg1=111&arg2=222"（同时也是约定好的需要拦截的）
                Uri uri = Uri.parse(url);

                // 如果url的协议 = 预先约定的 js 协议
                // 就往下解析参数
                if (uri.getScheme().equals("js")) {
                    // 如果 authority  = 预先约定协议里的 webview，即代表都符合约定的协议
                    // 所以拦截url,下面JS开始调用Android需要的方法
                    if (uri.getAuthority().equals("webview")) {
                        //  步骤3：
                        // 执行JS所需要调用的逻辑
//                        System.out.println("方法二、js调用了Android的方法");
//                        Log.e("Tag-setWebJsClient", url);

                        // 可以在协议上带有参数并传递到Android上
                        HashMap<String, String> map = new HashMap<>();
                        Set<String> collection = uri.getQueryParameterNames();
                        String callAndroid2_arg1 = uri.getQueryParameter("arg1");

                        //此处只能获取到key
                        Iterator<String> it = collection.iterator();
                        while (it.hasNext()) {
                            String str = it.next();
                            Log.e("Tag-setWebViewClient--:", str);
                        }

                        Log.e("Tag-setWebViewClient:", collection.toString());
                        Log.e("Tag-setWebViewClient-:", callAndroid2_arg1);

                    }
                    return true;
                }
                return super.shouldOverrideUrlLoading(view, url);
            }
        });

        //意思是：通过上述协议（就不需要JsToAndroid类了）来调用Android的方法，当想要返回值时比较麻烦
        //JS想要得到Android方法的返回值，只能通过 WebView 的 loadUrl()去执行 JS 方法把返回值传递回去
        // 实质就是Android调用JS的方法
//        mWebView.loadUrl("javascript:returnResult+('" + "Android中的值传给Js" + "')");//returnResult是Js中的一个方法

    }


    /************-------Js调用Android的方法：拦截Js对话框----------********************************************/

    //在Android 4.2版本之前采用拦截prompt（）进行漏洞修复。
    /**
     * 使用复杂：需要进行协议的约束。
     * 能满足绝大多数的互调场景
     */
    private void setWebChromeClient2() {
        mWebView.setWebChromeClient(new WebChromeClient(){
            /**
             * 拦截输入框
             * @param url
             * @param message 代表promt（）的内容（不是url）---这里的内容是协议【需要解析】
             * @param defaultValue
             * @param result 代表输入框的返回值
             */
            @SuppressLint("LongLogTag")
            @Override
            public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
                // 根据协议的参数，判断是否是所需要的url
                // 一般根据scheme（协议格式） & authority（协议名）判断（前两个参数）
                //假定传入进来的 url = "js://webview?arg1=111&arg2=222"（同时也是约定好的需要拦截的）
                Uri uri = Uri.parse(message);

                // 如果url的协议 = 预先约定的 js 协议
                // 就解析往下解析参数
                if (uri.getScheme().equals("js")) {
                    // 如果 authority  = 预先约定协议里的 webview，即代表都符合约定的协议
                    // 所以拦截url,下面JS开始调用Android需要的方法
                    if (uri.getAuthority().equals("demo")) {
                        // 执行JS所需要调用的逻辑
//                        Log.e("Tag-setWebChromeClient2", url);

                        // 可以在协议上带有参数并传递到Android上
                        HashMap<String, String> map = new HashMap<>();
                        Set<String> collection = uri.getQueryParameterNames();
                        String clickprompt_arg1 = uri.getQueryParameter("demoarg1");

                        //此处只能获取到key
                        for (String str : collection) {
                            Log.e("Tag-setWebChromeClient222:", str);
                        }

                        Log.e("Tag-setWebChromeClient2:", collection.toString());
                        Log.e("Tag-setWebChromeClient22:", clickprompt_arg1);

                        //参数result:代表消息框的返回值(输入值)
                        result.confirm("js的prompt被Android拦截了");
                    }
                    return true;
                }
                return super.onJsPrompt(view, url, message, defaultValue, result);
            }

            // 拦截JS的警告框
            @Override
            public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
                return super.onJsAlert(view, url, message, result);
            }


            // 拦截JS的确认框
            @Override
            public boolean onJsConfirm(WebView view, String url, String message, JsResult result) {
                return super.onJsConfirm(view, url, message, result);
            }
        });
    }



}
